#include <pthread.h>
#include <queue>
#include "Task.hpp"
using namespace std;
//#define DefalutNum 5

template<class T>
class BlockQueue
{
    static const int DefalutNum = 20;
public:

    // T pop()
    // {
    //     pthread_mutex_lock(_mutex);
    //     if(!_queue->empty())
    //     {
    //         cout << "consume: " << _queue->front() << endl;
    //         T temp = _queue->front();
    //         _queue->pop();
    //         pthread_mutex_unlock(_mutex);
    //         return temp;            
    //     }
    //     else
    //     {
    //         //pthread_cond_wait(_c_cond,_mutex);      
    //         //和signal的位置关系可以变吗？ -->不行！现象：换了之后consume不动了 
    //         //-->本质上是代码有问题，这里代码逻辑是：被唤醒后还没取呢就又直接通知生产者生产，而此时队列已满，所以生产者又通知消费者取，故卡住了！
    //         //被唤醒之后重新持有锁，继续往后执行，故被唤醒后应该执行取的逻辑！

    //         pthread_cond_signal(_p_cond);       //让生产者赶紧造
            
    //         pthread_mutex_unlock(_mutex);           //但先解锁再通知和先通知再解锁都可以！
    //     }
    // }

    /**********************************pop()这么写逻辑会更清晰：************************************/
    T pop()
    {
        pthread_mutex_lock(_mutex);
        while(_queue->empty())
        { 
            //pthread_cond_signal(_p_cond);   若这么写（push内也是这个顺序），不能说完全不对，只是逻辑变为了：先填满队列才能开始取走！
            pthread_cond_wait(_c_cond,_mutex);              
        }
        
        //等待完成直接取：
        //cout << "consume: " << _queue->front() << endl;
        T temp = _queue->front();
        _queue->pop();
        //if(_queue->size() <= _low_water)
            pthread_cond_signal(_p_cond);           //让生产者赶紧造       
        pthread_mutex_unlock(_mutex);           //先解锁再通知和先通知再解锁都可以！
        
        return temp; 
    }

    void push(const T& data)
    {
        pthread_mutex_lock(_mutex);  
        while(_queue->size() == _maxcap)
        {
            //pthread_cond_signal(_c_cond);      //解释见pop
            pthread_cond_wait(_p_cond,_mutex);     
        }
        //else
        //{
            //cout << "product: data-" << data <<endl;      //可以放这，但不要写else；因为若被阻塞了else就不会执行了！
            _queue->push(data);
        //}
        //if(_queue->size() >= _high_water)
            pthread_cond_signal(_c_cond);      //让消费者赶紧取
        pthread_mutex_unlock(_mutex);
    }

    BlockQueue(int num = DefalutNum)        //注意这里如果用指针要先分配空间才能调初始化函数，否则就相当于非法访问内存了！-->Segmentation fault
    :_maxcap(num),_p_cond(new pthread_cond_t),_c_cond(new pthread_cond_t),_mutex(new pthread_mutex_t),
    _low_water(num/3),_high_water(num*2/3)
    {
        pthread_cond_init(_p_cond,nullptr);
        pthread_cond_init(_c_cond,nullptr);
        pthread_mutex_init(_mutex,nullptr);
        _queue = new queue<T>();
    }
    
    ~BlockQueue()
    {
        pthread_cond_destroy(_p_cond);
        pthread_cond_destroy(_c_cond);
        pthread_mutex_destroy(_mutex);  
        delete _p_cond;
        delete _c_cond;
        delete _mutex;
        delete _queue;
    }

private:
    pthread_cond_t* _p_cond;    //生产者条件变量 
    pthread_cond_t* _c_cond;    //消费者条件变量
    pthread_mutex_t* _mutex;
    queue<T>* _queue;
    Task _t;
    int _low_water;     //低于水位线马上生产    用于定制生产消费的同步规则
    int _high_water;    //高于水位线马上取
    int _maxcap;     
};